package dfbz.com.dao.base;

import dfbz.com.annotation.MyField;
import dfbz.com.annotation.Table;
import dfbz.com.model.UserInfo;
import dfbz.com.utils.JDBCUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class BaseDao<T> {


    public List<T> findAll(Class<T> aClass) {
        //第一步 通过反射，拿到注解
        Table table = aClass.getDeclaredAnnotation(Table.class);
        String tableName = null;
        if (null != table) {
            //注解里面的值 就是表名
            tableName = table.value();
        }
        List<T> list = new ArrayList<>();
        if (null != tableName) {
            String sql = " select * from " + tableName;
            Connection connection = null;//数据库链接
            PreparedStatement statement = null;//sql预处理执行
            ResultSet resultSet = null;//sql执行完毕后返回的结果集
            //原生的JDBC
            try {
                //获取数据库链接
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(sql);
                resultSet = statement.executeQuery();
                while (resultSet.next()) {
                    //游标遍历数据库查处的一条条记录
                    Object obj = aClass.newInstance();// Student student = new Student();
                    //当遍历到第一条记录，后面都是用反射去获取Student的字段
                    Field[] fields = aClass.getDeclaredFields();
                    for (Field field : fields) {
                        String fieldName = field.getName();
                        String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                        MyField field1 = field.getDeclaredAnnotation(MyField.class);
                        if(null!=field1){
                            fieldName=field1.value();
                        }
                        //通过反射 获取set的方法
                        Method method = aClass.getDeclaredMethod(methodName, field.getType());
                        //通过反射 代理执行 本质上就等同于 student.setXXX(resultSet.getObject("XXX"));
                        method.invoke(obj, resultSet.getObject(fieldName));
                    }
                    // list.add(student)
                    list.add((T) obj);
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }

        return list;
    }

    public void add(T t) {
        Class<?> aClass = t.getClass();
        //第一步 通过反射，拿到注解
        Table table = aClass.getDeclaredAnnotation(Table.class);
        String tableName = null;
        if (null != table) {
            //注解里面的值 就是表名
            tableName = table.value();
        }
        Field[] fields = aClass.getDeclaredFields();
        StringBuilder sql = new StringBuilder();
        sql.append("insert into ").append(tableName).append(" values(");
        for (int i = 1; i <= fields.length; i++) {
            if (i == fields.length) {
                sql.append("?").append(")");
            } else {
                sql.append("?").append(",");
            }
        }


        Connection connection = null;//数据库链接
        PreparedStatement statement = null;//sql预处理执行
        try {
            connection = JDBCUtil.getConnection();
            statement = connection.prepareStatement(sql.toString());
            int index = 1;
            for (Field field : fields) {
                String fieldName = field.getName();
                String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                //通过反射 获取get的方法
                Method method = aClass.getDeclaredMethod(methodName);
                //通过反射 代理执行
                Object invoke = method.invoke(t);
                statement.setObject(index, invoke);
                index++;
            }

            statement.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, statement, connection);
        }

    }

    public void del(Object id, Class<T> aClass) {
        //第一步 通过反射，拿到注解
        Table table = aClass.getDeclaredAnnotation(Table.class);
        String tableName = null;
        if (null != table) {
            //注解里面的值 就是表名
            tableName = table.value();
        }
        String sql = "delete from " + tableName + " where id = ?";
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = JDBCUtil.getConnection();
            statement = connection.prepareStatement(sql);
            statement.setObject(1, id);
            statement.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, statement, connection);
        }
    }

    public T get(Object id, Class<T> aClass) {
        //第一步 通过反射，拿到注解
        Table table = aClass.getDeclaredAnnotation(Table.class);
        String tableName = null;
        if (null != table) {
            //注解里面的值 就是表名
            tableName = table.value();
        }
        String sql = "select * from " + tableName + " where id = ?";
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        T obj = null;
        try {
            connection = JDBCUtil.getConnection();
            statement = connection.prepareStatement(sql);
            statement.setObject(1, id);
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
                obj = aClass.newInstance();// Student student = new Student();
                //后面都是用反射去获取Student的字段
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    String fieldName = field.getName();
                    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //通过反射 获取set的方法
                    Method method = aClass.getDeclaredMethod(methodName, field.getType());
                    //通过反射 代理执行 本质上就等同于 student.setXXX(resultSet.getObject("XXX"));
                    method.invoke(obj, resultSet.getObject(fieldName));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, statement, connection);
        }
        return obj;
    }

    public void update(T t) {
        Class<?> aClass = t.getClass();
        //第一步 通过反射，拿到注解
        Table table = aClass.getDeclaredAnnotation(Table.class);
        String tableName = null;
        if (null != table) {
            //注解里面的值 就是表名
            tableName = table.value();
        }
        StringBuilder sql = new StringBuilder();
        sql.append("update ").append(tableName).append(" set id = ?,");
        Field[] fields = aClass.getDeclaredFields();
        try {
            for (Field field : fields) {
                String fieldName = field.getName();
                if(!"id".equals(fieldName)){
                    String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //通过反射 获取get的方法
                    Method method = aClass.getDeclaredMethod(methodName);
                    Object obj = method.invoke(t);
                    if(null != obj){
                        sql.append(fieldName).append(" = ?,");
                    }
                }
            }
            sql.delete(sql.lastIndexOf(","), sql.length());
            sql.append(" where id = ? ");
        } catch (Exception e) {
            e.printStackTrace();
        }

        Connection connection = null;
        PreparedStatement statement = null;
        try {
            int count = 0;
            connection = JDBCUtil.getConnection();
            statement = connection.prepareStatement(sql.toString());
            //因为ID 肯定是100%有的，service层会处理
            Method method = aClass.getDeclaredMethod("getId");
            Object id = method.invoke(t);
            statement.setObject(++count, id);
            for (Field field : fields) {
                String fieldName = field.getName();
                if(!"id".equals(fieldName)){
                    String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //通过反射 获取get的方法
                    Method getMethod = aClass.getDeclaredMethod(methodName);
                    Object obj = getMethod.invoke(t);
                    if(null != obj){
                        statement.setObject(++count, obj);
                    }
                }
            }
            statement.setObject(++count, id);
            statement.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(null, statement, connection);
        }

    }
}
